package com.mdb.data;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.bson.types.ObjectId;

import com.mdb.annotation.Data;
import com.mdb.config.MongoDBConfig;
import com.mdb.config.IDBConfig;
import com.mdb.util.ClassUtils;
import com.mongodb.BasicDBObject;

public abstract class BaseModel {
	// <字段名,字段类型的class>
	private static final Map<String, Class<?>> FIELD_MAP = new HashMap<String, Class<?>>();
	private static List<Class<?>> CLASS_LIST = new ArrayList<Class<?>>();
	public static final String ID = "_id";
	private BasicDBObject po = new BasicDBObject();
	private ObjectId id;

	protected BaseModel() {
	}

	protected BaseModel(BasicDBObject o) {
		this.id = o.getObjectId("_id");
		reflectGetAndSetModelClass(GS.SET, o);
	}

	protected void set(String name, Object value) {
		this.po.put(name, value);
	}

	public ObjectId getId() {
		return this.id;
	}

	protected void setId(ObjectId id) {
		this.id = id;
	}

	public abstract ITable getTable();

	public BasicDBObject getAndResetPo() {
		reflectGetAndSetModelClass(GS.GET, null);
		BasicDBObject o = this.po;
		this.po = new BasicDBObject();
		return o;
	}

	public boolean hasChanged() {
		return !this.po.isEmpty();
	}

	static {
		try {
			IDBConfig iDbConfig = MongoDBConfig.getDBConfig();
			CLASS_LIST = ClassUtils.findClasses(iDbConfig.getModelReflectPackage());
			for (Class<?> cs : CLASS_LIST) {
				for (Method m : cs.getMethods()){
					String methodName = m.getName();
					if(methodName.startsWith("get")) {
						FIELD_MAP.put(m.getName().replace("get", "").toLowerCase() , m.getReturnType());
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private Method getClassSetAndGetMethod(Class<?> cs, String field, GS gs) throws SecurityException, NoSuchMethodException {
		String firstLetter = field.substring(0, 1).toUpperCase();
		String remainLetter = field.substring(1);
		if (gs.getValue().equals(GS.GET.getValue())) {
			return cs.getMethod(gs.getValue() + firstLetter + remainLetter);
		} else {
			return cs.getMethod(gs.getValue() + firstLetter + remainLetter, FIELD_MAP.get(field));
		}
	}

	private void reflectGetAndSetModelClass(GS gs, BasicDBObject o) {
		try {
			for (Class<?> cs : CLASS_LIST) {
				if (!cs.isAnnotationPresent(Data.class)) {
					continue;
				}

				for (Method m : cs.getMethods()) {
					if (!m.getName().startsWith(gs.getValue())) {
						continue;
					}

					if (gs.getValue().equals(GS.GET.getValue())) {
						String field = m.getName().replace(gs.getValue(), "").toLowerCase();
						Object fieldValue = getClassSetAndGetMethod(cs, field, GS.GET).invoke(this);
						this.set(field, fieldValue);
					} else if (gs.getValue().equals(GS.SET.getValue())) {
						String field = m.getName().replace(gs.getValue(), "").toLowerCase();
						Object value = o.get(field);
						getClassSetAndGetMethod(cs, field, GS.SET).invoke(this, value);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	private enum GS {
		GET("get"), SET("set");
		private String value;

		private GS(String value) {
			this.value = value;
		}

		public String getValue() {
			return this.value;
		}
	}
}
